package services;

import dao.ContestJpaController;
import dao.exceptions.NonexistentEntityException;
import entities.Contest;
import entities.Problem;
import entities.User;
import exceptions.DatabaseException;
import exceptions.NonExistentObjectException;
import exceptions.PreexistingObjectException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import util.EnumSearchParameter;
import vo.VOContest;
import vo.VOScoreboard;
import vo.VOUser;

/**
 * @author sbaldrich
 */
public class ContestService {

    private static HashMap scoreboards;

    private static HashMap getScoreboards() {
        if (scoreboards == null) {
            scoreboards = new HashMap<String, HashMap<String, VOScoreboard>>();
        }
        return scoreboards;
    }
    private static ContestJpaController cc;

    public static ContestJpaController getInstance() {
        if (cc == null) {
            cc = new ContestJpaController();
        }
        return cc;
    }
    private static UserService us;

    private static UserService getUserService() {
        if (us == null) {
            us = new UserService();
        }
        return us;
    }
    private static SubmissionService ss;

    private static SubmissionService getSubmissionService() {
        if (ss == null) {
            ss = new SubmissionService();
        }
        return ss;
    }
    private static ProblemService ps;

    private static ProblemService getProblemService() {
        if (ps == null) {
            ps = new ProblemService();
        }
        return ps;
    }

    public void save(VOContest vo) throws Exception {
        cc = getInstance();
        if (find(vo.getContestCode()) != null) {
            throw new PreexistingObjectException("Contest Code");
        }
        Contest u = new Contest(vo);
        try {
            cc.create(u);
        } catch (Exception e) {
            throw new DatabaseException();
        }
    }

    public VOContest find(String contestCode) throws Exception {
        cc = getInstance();
        try {
            return cc.findContest(contestCode).getVO();
        } catch (Exception e) {
            return null;
        }
    }

    public Contest findObject(String contestCode) throws Exception {
        cc = getInstance();
        try {
            return cc.findContest(contestCode);
        } catch (Exception e) {
            return null;
        }
    }

    public void update(VOContest vo) throws Exception {
        cc = getInstance();
        Contest c = cc.findContest(vo.getIdContest());
        c.setContestCode(vo.getContestCode());
        c.setEnd(vo.getEnd());
        c.setName(vo.getName());
        c.setStart(vo.getStart());
        updateObject(c);
        
    }

    public void updateObject(Contest c) throws Exception {
        cc = getInstance();
        try {
            cc.edit(c);
        } catch (Exception e) {
            if (e instanceof NonexistentEntityException) {
                throw new NonExistentObjectException();
            }
            throw new DatabaseException("ContestService.update Operation");
        }
    }

    public void delete(VOContest vo) throws Exception {
        cc = getInstance();
        try {
            cc.destroy(vo.getIdContest());
        } catch (Exception e) {
            if (e instanceof NonexistentEntityException) {
                throw new NonExistentObjectException();
            }
            throw new DatabaseException("ContestService.delete Operation");
        }
    }

    public void addUser(String contestCode, String handle) throws Exception {
        us = getUserService();
        try {
            User u = us.findObject(handle);
            Contest c = findObject(contestCode);
            if (u == null) {
                throw new NonExistentObjectException("User");
            }
            if (c == null) {
                throw new NonExistentObjectException("Contest");
            }
            u.getContestList().add(c);
            c.getUserList().add(u);
            us.updateObject(u);
            updateObject(c);
        } catch (Exception e) {
            throw e;
        }
    }

    public ArrayList<VOScoreboard> getScoreboard(String contestCode) throws Exception {
        scoreboards = getScoreboards();
        if (!scoreboards.containsKey(contestCode)) {
            try {
                generateScoreboard(contestCode);
            } catch (Exception e) {
                System.out.println("Exception at ContestService.getScoreboard: " + e.getMessage());
                throw e;
            }
        }
        HashMap<String, VOScoreboard> scoreboard = (HashMap<String, VOScoreboard>) scoreboards.get(contestCode);
        Iterator it = scoreboard.keySet().iterator();
        ArrayList<VOScoreboard> ret = new ArrayList<VOScoreboard>();

        while (it.hasNext()) {
            ret.add((VOScoreboard) (scoreboard.get((String) it.next())));
        }

        Collections.sort(ret);
        return ret;

    }

    private void generateScoreboard(String contestCode) throws Exception {
        ss = getSubmissionService();
        scoreboards = getScoreboards();
        try {
            scoreboards.put(contestCode, new HashMap<String, VOScoreboard>());
            Contest c = findObject(contestCode);
            List<User> userlist = (List<User>) c.getUserList();
            int nproblems = c.getProblemList().size();
            for (User u : userlist) {
                ((HashMap<String, VOScoreboard>) scoreboards.get(contestCode)).put(u.getHandle(), new VOScoreboard(u.getHandle(), nproblems));
            }

        } catch (Exception e) {
            System.out.println("Exception at ContestService.generateScoreboard: " + e.getMessage());
            throw e;
        }
    }

    public void endContest(String contestCode) {
        ss = getSubmissionService();
        ps = getProblemService();
        try {
            Contest c = findObject(contestCode);
            List<Problem> problems = c.getProblemList();
            for (Problem p : problems) {
                p.setVisible(false);
                ps.updateObject(p);
            }
            updateRating(contestCode);
        } catch (Exception e) {
        }
    }

    private void updateRating(String contestCode) throws Exception {
        ArrayList<VOUser> userlist = us.findAll(EnumSearchParameter.CONTEST, contestCode);

    }

    public ArrayList<VOUser> updateRating(ArrayList<VOUser> scoreboard) {
        ArrayList<VOUser> expected = new ArrayList<VOUser>();
        for (VOUser v : scoreboard) {
            expected.add(v);
        }

        Collections.sort(expected);

        int p = 0, nR = expected.size() / 2 + 1;
        HashMap<String, Integer> delta = new HashMap<String, Integer>();
        HashMap<String, Integer> expPos = new HashMap<String, Integer>();
        for (int i = 0; i < expected.size(); i++) {
            if (expected.get(i).getRating() == 0) {
                expPos.put(expected.get(i).getHandle(), nR);
                continue;
            }
            if (i > 0) {
                if (expected.get(i).getRating() < expected.get(i - 1).getRating()) {
                    p++;
                }
            }
            expPos.put(expected.get(i).getHandle(), p);
        }


        for (int pos = 0; pos < expected.size(); pos++) {
            String actual = scoreboard.get(pos).getHandle();
            int totalDelta = 0;
            for (int other = 0; other < expected.size(); other++) {
                if (pos == other) {
                    continue;
                }
                String comp = scoreboard.get(other).getHandle();
                int expDelta = expPos.get((String) (comp)) - expPos.get((String) actual);
                int realDelta = other - pos;
                totalDelta += (realDelta - expDelta);
            }
            delta.put(actual, totalDelta);
        }


        for (VOUser u : scoreboard) {
            if (u.getRating() == -1) {
                u.setRating(1000);
            }
            u.setRating(u.getRating() + (delta.get(u.getHandle())));
        }

        return scoreboard;
    }

    public ArrayList<VOContest> findAll() {
        cc = getInstance();
        try {
            Vector<Contest> l = null;
            l = (Vector<Contest>) cc.findContestEntities();
            if (l.isEmpty() || l == null) {
                return null;
            }
            ArrayList<VOContest> ret = new ArrayList<VOContest>();
            for (Contest c : l) {
                ret.add(c.getVO());
            }
            return ret;
        } catch (Exception e) {
            
            return null;
        }
    }

    public ArrayList<VOContest> findAll(EnumSearchParameter parameter, String value) throws Exception {
        if (!parameter.equals(EnumSearchParameter.USER)) {
            return null;
        }
        us = getUserService();
        User u = null;
        try {
            u = us.findObject(value);

        } catch (Exception e) {
            throw new DatabaseException();
        }
        if (u == null) {
            return null;
        }
        ArrayList<Contest> l = (ArrayList<Contest>) u.getContestList();
        if (l.isEmpty() || l == null) {
            return null;
        }
        ArrayList<VOContest> ret = new ArrayList<VOContest>();
        for (Contest c : l) {
            ret.add(c.getVO());
        }
        return ret;

    }
}
